package com.zcy.auth.service.impl;

import com.zcy.auth.domain.Permission;
import com.zcy.auth.service.IPermissionScanService;
import com.zcy.auth.service.IPermissionService;
import com.zcy.basic.annotation.PermissionApi;
import com.zcy.basic.enums.RequestTypeEnum;
import com.zcy.basic.util.ClassUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * <p>
 *
 * </p>
 *
 * @author lucker
 * @date 2023/12/1 15:09
 */
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class PermissionScanServiceImpl implements IPermissionScanService {

    @Value("${permission.controller-location}" )
    private String controllerLocation;

    private static final List<Class> requestTypeSet = new ArrayList<>(
            Arrays.asList(
                    GetMapping.class,
                    PostMapping.class,
                    PutMapping.class,
                    DeleteMapping.class,
                    PatchMapping.class,
                    RequestMapping.class
            )
    );

    @Autowired
    private IPermissionService permissionService;

    @Transactional
    @Override
    public void scan() {
        // 获取所有Controller
        List<String> allControllerName = ClassUtils.getAllClassName(controllerLocation);
        // 记录所有的父级权限
        List<Permission> parentPermissionList = new ArrayList<>();
        List<List<Permission>> childPermissionList = new ArrayList<>();
//        int i = 0;
        // 遍历所有controller
        for (String controllerName : allControllerName) {
            Class<?> aClass = null;
            try {
                aClass = Class.forName(controllerName);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            // 获取Controller的注解
            RequestMapping requestMappingsAnnotation = aClass.getAnnotation(RequestMapping.class);
            PermissionApi permissionApiAnnotation = aClass.getAnnotation(PermissionApi.class);
            // 判断是否有 RequestMapping 和 PermissionApi 注解
            if (Objects.nonNull(requestMappingsAnnotation) && Objects.nonNull(permissionApiAnnotation)) {
                // 封装父permission并记录
                String parentPermissionName = permissionApiAnnotation.name();
                String parentPermissionDescription = permissionApiAnnotation.description();
                String parentPermissionUrl = requestMappingsAnnotation.value()[0];
                String parentPermissionSn = aClass.getSimpleName();
                Permission parentPermission = new Permission().setName(parentPermissionName)
                        .setDesc(parentPermissionDescription)
                        .setUrl(parentPermissionUrl)
                        .setSn(parentPermissionSn);
                parentPermissionList.add(parentPermission);
                // 使用 getDeclaredMethods 可以排除Object类中自带的一些方法
                Method[] apiMethods = aClass.getDeclaredMethods();
                List<Permission> methodPermissionList = new ArrayList<>();
                for (Method apiMethod : apiMethods) {
                    // 获取权限注解
                    PermissionApi methodPermissionApiAnnotation = apiMethod.getAnnotation(PermissionApi.class);
                    // 如果权限注解不为空
                    if (Objects.nonNull(methodPermissionApiAnnotation)) {
                        // 获取URL
                        String methodPermissionUrl = getMethodUrl(apiMethod);
                        String methodPermissionName = methodPermissionApiAnnotation.name();
                        // 如果方法权限描述为空则使用方法权限名称
                        String methodPermissionDescription = methodPermissionApiAnnotation.description().isEmpty() ? methodPermissionName : methodPermissionApiAnnotation.description();
                        // 封装permission并记录
                        Permission methodPermission = new Permission().setName(methodPermissionName)
                                .setDesc(methodPermissionDescription)
                                .setUrl(parentPermissionUrl + methodPermissionUrl)
                                .setSn(parentPermissionSn + ":" + apiMethod.getName());
                        methodPermissionList.add(methodPermission);
                    }
                }
                childPermissionList.add(methodPermissionList);
//                System.out.println("parentPermission==" + parentPermission);
            }
            /*childPermissionList.get(i).forEach(System.out::println);
            i++;*/
        }
        // 删除所有权限
        permissionService.deleteAll();
        // 保存权限
        permissionService.addBatch(parentPermissionList);
        int i = 0;
        List<Permission> allChildPermissionList = new ArrayList<>();
        for (Permission permission : parentPermissionList) {
            for (Permission childPermission : childPermissionList.get(i)) {
//                childPermission.setParentId(permission.getId());
                childPermission.setParent(new Permission().setId(permission.getId()));
                allChildPermissionList.add(childPermission);

            }
            i++;
        }
        permissionService.addBatch(allChildPermissionList);

    }

    private String getMethodUrl (Method method) {
        String methodUrl = "";
        RequestTypeEnum[] requestTypeEnums = RequestTypeEnum.values();
        for (RequestTypeEnum requestTypeEnum : requestTypeEnums) {
            Annotation annotation = method.getAnnotation(requestTypeEnum.getRequestType());
            if (Objects.nonNull(annotation)) {
                try {
                    Method valueMethod = annotation.annotationType().getMethod("value" );
                    String[] urls = (String[]) valueMethod.invoke(annotation);
                    if(urls != null && urls.length > 0){
                        methodUrl = urls[0];
                        break;
                    }
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return methodUrl;
    }
}
